#define DOHOME_LOG_LVL          DOHOME_LOG_LVL_INFO
#define DOHOME_LOG_TAG          "doit_timed"

#include <dohome_log.h>

#include <doit_timed_task.h>

#include <dohome_api.h>

// #define printf(...)
#define COUNTDOWN_NOT_USE                     0xFFFFFFFF

DOHOME_STATIC DOHOME_UINT8_T save_timed_list = 0;

DOHOME_STATIC doit_timer_task_cb_t timer_task_cb = NULL;
DOHOME_STATIC doit_countdown_task_cb_t countdown_task_cb = NULL;

DOHOME_STATIC DOHOME_UINT32_T countdown[DOIT_MAX_COUNTDOWN_NUM] = {COUNTDOWN_NOT_USE}; //0xFFFFFFFF
doit_timer_task_t timer_task_list[DOIT_MAX_COUNTDOWN_NUM];

DOHOME_UINT32_T doit_get_countdown(DOHOME_UINT8_T num){
    DOHOME_UINT32_T curr_timestamp = 0;
    
    if(countdown[num] == COUNTDOWN_NOT_USE){
        return 0;
    }else{
        curr_timestamp = dohome_time_get_time();
        return countdown[num] - curr_timestamp;
    }
}

dohome_op_ret doit_set_countdown(DOHOME_UINT8_T num, DOHOME_UINT32_T second){
    DOHOME_UINT32_T curr_timestamp = 0;

    if(num > DOIT_MAX_COUNTDOWN_NUM-1){
        return OPRT_INVALID_PARM;
    }

    if(second > 86400){
        return OPRT_INVALID_PARM;
    }

    DOHOME_LOG_I("countdown%d set second:%d", num, second);

    if(second == 0){
        countdown[num] = COUNTDOWN_NOT_USE;
    }else{
        curr_timestamp = dohome_time_get_time();
        countdown[num] = curr_timestamp + second;
    }
    return OPRT_OK;
}

dohome_op_ret doit_reset_countdown(DOHOME_UINT8_T num){
    if(countdown[num] == COUNTDOWN_NOT_USE){
        return OPRT_COM_ERROR;
    }
    countdown[num] = COUNTDOWN_NOT_USE;
    return OPRT_OK;
}

dohome_op_ret doit_set_timer(DOHOME_UINT8_T num, doit_timer_t *list, DOHOME_UINT8_T list_num){

    DOHOME_UINT8_T i = 0;

    if(num > DOIT_MAX_TIMER_LIST_NUM-1){
        return OPRT_INVALID_PARM;
    }

    for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++)
    {
        if(i < list_num){
            list[i].use = 1;
            list[i].active = 1;
            memcpy(&timer_task_list[num].list[i], &list[i], sizeof(doit_timer_t));
        }else{
            timer_task_list[num].list[i].use = 0;
            timer_task_list[num].list[i].on = 0;
            timer_task_list[num].list[i].type = 0;
            timer_task_list[num].list[i].active = 0;
            timer_task_list[num].list[i].timestamp = 0;
        }
    }
    return OPRT_OK;
}

DOHOME_STATIC DOHOME_UINT32_T hexstr_to_uint32(DOHOME_CHAR_T *str, DOHOME_UINT16_T len)
{
 	DOHOME_UINT16_T i, m, n;
	DOHOME_UINT32_T temp = 0;
	for (i = 0; i<len; i++)
	{
		if (str[i] >= 'A'&&str[i] <= 'F')
			n = str[i] - 'A' + 10;
		else if (str[i] >= 'a'&&str[i] <= 'f')
			n = str[i] - 'a' + 10;
		else n = str[i] - '0';
		temp = temp * 16 + n;
	}
	return temp;
}

dohome_op_ret doit_set_timer_to_str(DOHOME_UINT8_T num, DOHOME_CHAR_T *str){

    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T on = 0;
    DOHOME_UINT8_T tp = 0;
    DOHOME_UINT32_T tm = 0; 
    DOHOME_UINT8_T timer_size = 0;
    doit_timer_t list[DOIT_MAX_TIMER_LIST_ITEM_NUM] = {0};
     
    if(str == NULL){
        return OPRT_INVALID_PARM;
    }

    if(dohome_safe_strlen(str, DOIT_MAX_TIMER_LIST_ITEM_NUM*10+1) != DOIT_MAX_TIMER_LIST_ITEM_NUM*10){
        return OPRT_INVALID_PARM;
    }

    for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++)
    {
        on = hexstr_to_uint32(str, 1);
        tp = hexstr_to_uint32(str+1, 1);
        tm = hexstr_to_uint32(str+2, 8);
        str = str + 10;
        if(tm != 0){
            DOHOME_LOG_I("list_id :%d, add timer item%d on: %d, tp: %d, tm: %ld", num, i+1, on, tp, tm);
            list[i].on = on;
            list[i].type = tp;
            list[i].timestamp = tm;
            timer_size++;
        }else{
            break;
        }
    }
    doit_set_timer(num, list, timer_size);
    save_timed_list = 1;
    return OPRT_OK;
}

DOHOME_STATIC DOHOME_CHAR_T doit_timer_str[DOIT_MAX_TIMER_LIST_ITEM_NUM*10+1];
DOHOME_CHAR_T *doit_get_timer_str(DOHOME_UINT8_T num){
    DOHOME_UINT8_T i = 0;
    DOHOME_CHAR_T *str_p = doit_timer_str;

    if(num > DOIT_MAX_TIMER_LIST_NUM-1){
        return "0000000000";
    }
    for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++)
    {
        if(timer_task_list[num].list[i].use == 1){
            sprintf(str_p, "%01X%01X%08lX", timer_task_list[num].list[i].on, timer_task_list[num].list[i].type, timer_task_list[num].list[i].timestamp);
        }else{
            strcpy(str_p, "0000000000");
        }
        str_p = str_p + 10;
    }
    *(str_p) = '\0';
    return doit_timer_str;
}

dohome_op_ret doit_timer_list_save(void){

    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T k = 0;

    DOHOME_LOG_I("save timer_list");
    dohome_kvs_set_env_blob("dt_timer_list", timer_task_list, sizeof(timer_task_list));

    for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
        for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++){
            if(timer_task_list[k].list[i].timestamp != 0){
                DOHOME_LOG_I("timer%d item%d on: %d, tp: %d, tm: %d", k, i+1, timer_task_list[k].list[i].on, timer_task_list[k].list[i].active, timer_task_list[k].list[i].timestamp);
            }
        }
    }
    return OPRT_OK;
}

void doit_reset_timed_task(void){
    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T k = 0;
    for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
        for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++){
            timer_task_list[k].list[i].use = 0;
            timer_task_list[k].list[i].on = 0;
            timer_task_list[k].list[i].type = 0;
            timer_task_list[k].list[i].active = 0;
            timer_task_list[k].list[i].timestamp = 0;
        }
    }
    DOHOME_LOG_I("save timer_list");
    dohome_kvs_set_env_blob("dt_timer_list", timer_task_list, sizeof(timer_task_list));

    for (i = 0; i < DOIT_MAX_COUNTDOWN_NUM; i++){
        countdown[i] = COUNTDOWN_NOT_USE;
    }

}

dohome_op_ret doit_timer_list_read(void){

    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T k = 0;
    DOHOME_SIZE_T len = 0;
    len = dohome_kvs_get_env_blob("dt_timer_list", timer_task_list, sizeof(timer_task_list), NULL);
    if(len != sizeof(timer_task_list)){
        DOHOME_LOG_D("read dt_timer_list error");
        for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
            for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++){
                timer_task_list[k].list[i].use = 0;
                timer_task_list[k].list[i].on = 0;
                timer_task_list[k].list[i].type = 0;
                timer_task_list[k].list[i].active = 0;
                timer_task_list[k].list[i].timestamp = 0;
            }
        }
    }
    for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
        for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++){
            if(timer_task_list[k].list[i].timestamp != 0){
                DOHOME_LOG_I("timer%d item%d on: %d, tp: %d, tm: %d", k, i+1, timer_task_list[k].list[i].on, timer_task_list[k].list[i].active, timer_task_list[k].list[i].timestamp);
            }
        }
    }
    
    return OPRT_OK;
}

void doit_timer_list_active(void){
    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T k = 0;
    for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
        for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++){
            timer_task_list[k].list[i].active = 1;
        }
    }
}

dohome_op_ret doit_timed_task_init(void){
    DOHOME_UINT8_T i = 0;
    for (i = 0; i < DOIT_MAX_COUNTDOWN_NUM; i++){
        countdown[i] = COUNTDOWN_NOT_USE;
    }
    doit_timer_list_read();
    doit_timer_list_active();
    return OPRT_OK;
}

void doit_timed_task_set_cb(doit_timer_task_cb_t cb){
    timer_task_cb = cb;
}

void doit_countdown_task_set_cb(doit_countdown_task_cb_t cb){
    countdown_task_cb = cb;
}

void doit_timed_task_loop(void){

    DOHOME_UINT8_T i = 0;
    DOHOME_UINT8_T k = 0;
    DOHOME_UINT32_T curr_timestamp = 0;
    DOHOME_UINT32_T curr_day_time = 0;
    DOHOME_UINT32_T timer_day_time = 0;

    DOHOME_STATIC DOHOME_UINT32_T change_day = 0;
    DOHOME_STATIC DOHOME_UINT32_T last_timestamp = 0;
    
    curr_timestamp = dohome_time_get_time();
    // DOHOME_LOG_D("curr_timestamp: %ld\n", curr_timestamp);

    if(curr_timestamp > 1601481600){

        if(save_timed_list){
            save_timed_list = 0;
            doit_timer_list_save();
        }

        if(curr_timestamp%86400 == 0){
            if(change_day == 0){
                change_day = 1;
                doit_timer_list_active();
            }
        }else{
            change_day = 0;
        }
        
        for (k = 0; k < DOIT_MAX_TIMER_LIST_NUM; k++){
            for (i = 0; i < DOIT_MAX_TIMER_LIST_ITEM_NUM; i++)
            {
                if(timer_task_list[k].list[i].use && timer_task_list[k].list[i].on && timer_task_list[k].list[i].active){
                    if(timer_task_list[k].list[i].type == DOIT_TIMER_ONE_CLOSE || timer_task_list[k].list[i].type == DOIT_TIMER_ONE_OPEN){
                        DOHOME_LOG_D("wait one tm: %ld curr_tm: %d", timer_task_list[k].list[i].timestamp, curr_timestamp);
                        if((timer_task_list[k].list[i].timestamp <= curr_timestamp) && (curr_timestamp <= (timer_task_list[k].list[i].timestamp+60))){
                            timer_task_list[k].list[i].on = 0;
                            timer_task_list[k].list[i].active = 0;
                            if(timer_task_list[k].list[i].type == DOIT_TIMER_ONE_CLOSE){
                                DOHOME_LOG_I("one timer%d %ld -> close", i+1, timer_task_list[k].list[i].timestamp);
                                if(timer_task_cb){
                                    timer_task_cb(k, 0);
                                }
                            }else if(timer_task_list[k].list[i].type == DOIT_TIMER_ONE_OPEN){
                                DOHOME_LOG_I("one timer%d %ld -> open", i+1, timer_task_list[k].list[i].timestamp);
                                if(timer_task_cb){
                                    timer_task_cb(k, 1);
                                }
                            }
                            // doit_timer_list_save();
                            save_timed_list = 1;
                        }
                    }else if(timer_task_list[k].list[i].type == DOIT_TIMER_REPEAT_CLOSE || timer_task_list[k].list[i].type == DOIT_TIMER_REPEAT_OPEN){
                        curr_day_time = curr_timestamp%86400;
                        timer_day_time = timer_task_list[k].list[i].timestamp%86400;
                        DOHOME_LOG_D("wait repeat tm: %ld curr_tm: %d", timer_day_time, curr_day_time);
                        if((timer_day_time <= curr_day_time) && (curr_day_time <= (timer_day_time+60))){
                            timer_task_list[k].list[i].active = 0;
                            if(timer_task_list[k].list[i].type == DOIT_TIMER_REPEAT_CLOSE){
                                DOHOME_LOG_I("repeat timer%d %ld -> close", i+1, timer_task_list[k].list[i].timestamp);
                                if(timer_task_cb){
                                    timer_task_cb(k, 0);
                                }
                            }else if(timer_task_list[k].list[i].type == DOIT_TIMER_REPEAT_OPEN){
                                DOHOME_LOG_I("repeat timer%d %ld -> open", i+1, timer_task_list[k].list[i].timestamp);
                                if(timer_task_cb){
                                    timer_task_cb(k, 1);
                                }
                            }
                            // doit_timer_list_save();
                        }
                    }
                }
            } 
        }
    }

    for (i = 0; i < DOIT_MAX_COUNTDOWN_NUM; i++)
    {
        if(countdown[i] < curr_timestamp && curr_timestamp > 1601481600){
            countdown[i] = countdown[i] - last_timestamp + curr_timestamp;
        }

        if(countdown[i] != COUNTDOWN_NOT_USE){
            if(countdown[i]-curr_timestamp == 0){
                countdown[i] = COUNTDOWN_NOT_USE;
                DOHOME_LOG_I("countdown%d -> timeout", i);
                if(countdown_task_cb){
                    countdown_task_cb(i);
                }
            }else{
                DOHOME_LOG_D("countdown: %d", countdown[i]-curr_timestamp);
            }
        }
    }

    last_timestamp = curr_timestamp;
}
